package cn.iocoder.yudao.module.iot.controller.app.framework.aspect;

import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.iot.controller.admin.operationhistory.vo.OperationHistorySaveReqVO;
import cn.iocoder.yudao.module.iot.service.operationhistory.OperationHistoryService;
import io.swagger.v3.oas.annotations.Operation;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class OperationHistoryAspect {

    @Autowired
    private OperationHistoryService operationHistoryService;

    private final ObjectMapper objectMapper;

    public OperationHistoryAspect() {
        this.objectMapper = new ObjectMapper();
        this.objectMapper.registerModule(new JavaTimeModule());
        this.objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    }

    // 定义切入点：cn.iocoder.yudao.module.iot.controller.app包下的所有方法，并且带有@Operation注解
    @Pointcut("execution(* cn.iocoder.yudao.module.iot.controller.app..*.*(..)) && @annotation(io.swagger.v3.oas.annotations.Operation)")
    public void iotAppControllerOperation() {}

    @Around("iotAppControllerOperation()")
    public Object recordOperation(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
                .currentRequestAttributes()).getRequest();

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Operation operation = method.getAnnotation(Operation.class);

        if (StringUtils.isBlank(operation.description())) {
            return joinPoint.proceed();
        }
        // 创建操作历史记录
        OperationHistorySaveReqVO history = new OperationHistorySaveReqVO();
        history.setOperationCode(request.getRequestURI());
        history.setOperationName(operation.summary());
        history.setOperationDescription(operation.description());
        history.setHttpMethod(request.getMethod());
        history.setEndpoint(request.getRequestURI());

        // 设置用户信息（从安全上下文获取）
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            HashMap userInfo = (HashMap)((LoginUser)authentication.getPrincipal()).getInfo();
            history.setUserId(getUserId(authentication));
            history.setUserName(userInfo.get("nickname").toString());
        }

        // 设置客户端信息
        history.setIpAddress(getClientIpAddress(request));
        history.setUserAgent(request.getHeader("User-Agent"));

        // 记录请求参数和请求体
        history.setParameters(getRequestParameters(joinPoint, method));
        history.setRequestBody(getRequestBody(joinPoint, method));

        long startTime = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();

            // 记录响应信息
            history.setResponseStatus(HttpStatus.OK.value());
            history.setResponseBody(serializeResponse(result));
            history.setExecutionTimeMs(endTime - startTime);
            history.setStatus("SUCCESS");

            return result;
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();

            // 记录错误信息
            history.setResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            history.setErrorMessage(e.getMessage());
            history.setExecutionTimeMs(endTime - startTime);
            history.setStatus("FAILED");

            throw e;
        } finally {
            Long tenantId = Long.parseLong(request.getHeader("tenant-id"));
            // 保存操作历史记录
            TenantUtils.execute(tenantId, () -> {
                operationHistoryService.createOperationHistory(history);
            });
        }
    }

    /**
     * 获取请求参数
     */
    private String getRequestParameters(ProceedingJoinPoint joinPoint, Method method) {
        try {
            Map<String, Object> parameters = new HashMap<>();
            Object[] args = joinPoint.getArgs();
            Parameter[] methodParameters = method.getParameters();

            for (int i = 0; i < methodParameters.length; i++) {
                Parameter parameter = methodParameters[i];
                Object arg = args[i];

                // 跳过HttpServletRequest、HttpServletResponse等特殊参数
                if (arg instanceof HttpServletRequest ||
                        arg instanceof HttpServletResponse ||
                        arg instanceof Authentication) {
                    continue;
                }

                // 跳过文件上传参数
                if (arg instanceof MultipartFile) {
                    MultipartFile file = (MultipartFile) arg;
                    Map<String, Object> fileInfo = new HashMap<>();
                    fileInfo.put("fileName", file.getOriginalFilename());
                    fileInfo.put("size", file.getSize());
                    fileInfo.put("contentType", file.getContentType());
                    parameters.put(parameter.getName(), fileInfo);
                    continue;
                }

                // 处理其他参数
                parameters.put(parameter.getName(), arg);
            }

            return objectMapper.writeValueAsString(parameters);
        } catch (Exception e) {
            return "Failed to serialize parameters: " + e.getMessage();
        }
    }

    /**
     * 获取请求体
     */
    private String getRequestBody(ProceedingJoinPoint joinPoint, Method method) {
        try {
            Object[] args = joinPoint.getArgs();
            Parameter[] parameters = method.getParameters();

            // 查找带有@RequestBody注解的参数
            for (int i = 0; i < parameters.length; i++) {
                if (parameters[i].isAnnotationPresent(org.springframework.web.bind.annotation.RequestBody.class)) {
                    Object requestBody = args[i];

                    // 跳过HttpServletRequest等特殊参数
                    if (requestBody instanceof HttpServletRequest ||
                            requestBody instanceof HttpServletResponse) {
                        continue;
                    }

                    return objectMapper.writeValueAsString(requestBody);
                }
            }

            return null;
        } catch (Exception e) {
            return "Failed to serialize request body: " + e.getMessage();
        }
    }

    /**
     * 序列化响应
     */
    private String serializeResponse(Object result) {
        try {
            if (result == null) {
                return null;
            }

            // 如果响应已经是字符串，直接返回
            if (result instanceof String) {
                return (String) result;
            }

            // 序列化对象为JSON字符串
            return objectMapper.writeValueAsString(result);
        } catch (Exception e) {
            return "Failed to serialize response: " + e.getMessage();
        }
    }

    /**
     * 获取用户ID
     */
    private Long getUserId(Authentication authentication) {
        try {
            // 假设用户主体有一个getId方法
            Object principal = authentication.getPrincipal();
            if (principal instanceof org.springframework.security.core.userdetails.UserDetails) {
                // 这里需要根据实际的UserDetails实现来获取用户ID
                // 例如，如果您的UserDetails实现有一个getId方法:
                 return ((LoginUser) principal).getId();
            }

            // 如果无法获取用户ID，返回null
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}